#include <TensorFlowLite.h> // 包含TensorFlow Lite库头文件
#include "main_functions.h" // 包含主要函数头文件

#include "tensorflow/lite/micro/all_ops_resolver.h" // 包含所有操作解析器头文件
#include "constants.h" // 包含常量定义头文件
#include "model.h" // 包含模型头文件
#include "output_handler.h" // 包含输出处理头文件
#include "tensorflow/lite/micro/micro_error_reporter.h" // 包含微型错误报告器头文件
#include "tensorflow/lite/micro/micro_interpreter.h" // 包含微型解释器头文件
#include "tensorflow/lite/schema/schema_generated.h" // 包含模式生成头文件
#include "tensorflow/lite/version.h" // 包含版本头文件

// 全局变量,用于与Arduino样式 SKETCH 的兼容性。
namespace {
tflite::ErrorReporter* error_reporter = nullptr; // 定义错误报告指针,初始化为nullptr
const tflite::Model* model = nullptr; // 定义模型常量指针,初始化为nullptr
tflite::MicroInterpreter* interpreter = nullptr; // 定义解释器指针,初始化为nullptr
TfLiteTensor* input = nullptr; // 定义输入张量指针,初始化为nullptr
TfLiteTensor* output = nullptr; // 定义输出张量指针,初始化为nullptr
int inference_count = 0; // 定义推理次数变量,初始化为0

constexpr int kTensorArenaSize = 2000; // 定义张量内存池大小常量
uint8_t tensor_arena[kTensorArenaSize]; // 定义张量内存池数组
}

// 函数名对Arduino兼容性非常重要。
void setup() { // 设置函数
// 设置日志。谷歌的风格是避免全局变量或静态变量,因为存在生命周期不确定性,但因为这个销毁函数简单,所以可以。
// NOLINTNEXTLINE(runtime-global-variables)
static tflite::MicroErrorReporter micro_error_reporter; // 定义静态微型错误报告器
error_reporter = &micro_error_reporter; // 设置错误报告器指向微型错误报告器

// 将模型映射到一个可用的数据结构。这不涉及任何复制或解析,是一个非常轻量级的操作。
model = tflite::GetModel(g_model); // 获取模型赋值给model
if (model->version() != TFLITE_SCHEMA_VERSION) { // 如果模型版本与架构版本不匹配
	TF_LITE_REPORT_ERROR(error_reporter,
		"Model provided is schema version %d not equal "
                         	"to supported version %d.",
		model->version(), TFLITE_SCHEMA_VERSION);
	return;
}

// 这将获取我们需要的所有操作实现。
// NOLINTNEXTLINE(runtime-global-variables)
static tflite::AllOpsResolver resolver; // 定义静态全操作解析器

// 构建一个解释器来运行模型。
static tflite::MicroInterpreter static_interpreter(
model, resolver, tensor_arena, kTensorArenaSize, error_reporter); // 创建静态解释器
interpreter = &static_interpreter; // 设置解释器指向静态解释器

// 从tensor_arena为模型的张量分配内存。
TfLiteStatus allocate_status = interpreter->AllocateTensors(); // 分配张量内存
if (allocate_status != kTfLiteOk) { // 如果内存分配失败
	TF_LITE_REPORT_ERROR(error_reporter, "AllocateTensors() failed");
	return;
}

// 获取模型输入和输出张量的指针。
input = interpreter->input(0); // 获取输入张量
output = interpreter->output(0); // 获取输出张量

// 跟踪执行了多少次推理。
inference_count = 0; // 推理次数置0
}

// 函数名对Arduino兼容性非常重要。

void loop() { // 主循环函数
// 计算一个要喂入模型的 x 值。我们将当前的推理次数与每周期推理次数进行比较,
// 以确定在模型训练的所有可能 x 值范围内的位置,并用此计算一个值。
	float position = static_cast<float>(inference_count) /
	                         static_cast<float>(kInferencesPerCycle); // 计算位置
	float x = position * kXrange; // 根据位置计算 x 值

	// 将浮点输入量化为整数
	int8_t x_quantized = x / input->params.scale + input->params.zero_point; // 量化 x
	// 将量化后的输入放入模型的输入张量
	input->data.int8[0] = x_quantized; // 设置量化输入到输入张量

	// 运行推理,并报告任何错误
	TfLiteStatus invoke_status = interpreter->Invoke(); // 运行推理
	if (invoke_status != kTfLiteOk) { // 如果推理失败
		TF_LITE_REPORT_ERROR(error_reporter, "Invoke failed on x: %f\n",
			static_cast<double>(x));
		return;
	}

	// 从模型的输出张量获取量化后的输出
	int8_t y_quantized = output->data.int8[0]; // 获取量化后的输出
	// 将整数输出反量化为浮点数
	float y = (y_quantized - output->params.zero_point) * output->params.scale; // 反量化

	// 输出结果。可以为每个受支持的硬件目标实现自定义的 HandleOutput 函数。
	HandleOutput(error_reporter, x, y); // 处理并输出结果

	// 推理次数加1,如果达到周期总数则重置为0
	inference_count += 1;

	if (inference_count >= kInferencesPerCycle) inference_count = 0;
}
